昨天講完為什麼架構審查需要獨立設計,今天用一個具體案例,把「一般 code review 看不出問題、架構審查一查就抓到」這件事走一遍。
情境是這樣:系統裡有一個負責驗證訂單資料的模組,屬於架構裡最底層的資料驗證層——理論上它不該知道任何跟「怎麼通知使用者」有關的事。有一次,一個實作 agent 接到任務:「訂單金額超過門檻時,驗證失敗要立刻通知客服」。
agent 看了現有程式碼,發現驗證失敗的判斷邏輯剛好就寫在這個底層模組裡,最快的修法是直接在驗證失敗的那一行後面,呼叫上層的通知服務把訊息送出去。程式碼能動,單元測試也照樣通過——因為測試驗證的是「金額超過門檻時,驗證回傳失敗、且通知服務有被呼叫」,兩件事都成立。
這次改動照常送出去做 code review,委派給 AI 的指令是「看看這段改動有沒有問題」。AI 檢查了邏輯(門檻判斷正確)、檢查了測試(斷言涵蓋了通知有沒有觸發),沒有發現異常,直接放行。
問題不在 AI 讀漏了什麼,而在於它從沒被要求去查證「這個模組能不能依賴那個模組」這件事。 底層模組呼叫上層服務,這在程式碼的正確性上完全合理——語法沒錯、邏輯沒錯、測試也綠燈。唯一「不對」的地方,是這件事跟系統的分層規則衝突:資料驗證層不該知道通知服務的存在,這條依賴方向一旦成立,以後任何人想要獨立測試、獨立替換這個驗證模組,都得先處理掉這個通知服務的依賴,即使那次改動根本不關心通知邏輯。
這個依賴後來被獨立的架構審查角色抓到。它拿到的輸入不是單純的 diff,而是 diff 加上這個專案的分層規則說明(哪一層可以依賴哪一層)。查證步驟很直接:先確認這次改動新增了什麼依賴關係,再對照分層規則,看這個依賴方向合不合規。結果立刻浮現——底層驗證模組依賴了上層通知服務,方向反了。
用一組對照來看兩種審查的差異:
❌ 一般 code review:
「這段改動邏輯正確,測試涵蓋了新增的通知行為,予以放行。」
→ 只查證了「這段程式碼做的事對不對」,
沒有查證「這段程式碼放的位置對不對」
✅ 架構審查:
「這次改動讓資料驗證模組(底層)直接呼叫了通知服務(上層),
違反本專案『底層不得依賴上層』的分層規則。
建議改法:驗證模組只回傳驗證結果,
由呼叫驗證模組的上層程式碼決定要不要觸發通知,
而不是把通知邏輯塞進驗證模組本身。」
→ 帶著分層規則去查證依賴方向,
抓到了邏輯正確性審查看不到的問題
「程式碼能動」是最低標準,不是架構合不合格的判斷依據。 這次修法之所以會發生,是因為對 agent 來說,「讓通知在驗證失敗時觸發」是眼前唯一的目標,它沒有理由主動去查證「這樣寫會不會把兩個本該獨立的層次绑在一起」——除非有人明確要求它做這個查證。這正是這個系列反覆講的模式:AI 給出的「這樣改沒問題」,查證範圍只覆蓋它被要求查證的部分,依賴方向這種需要額外脈絡的維度,不會自動被涵蓋進去。
修法也不複雜:把驗證模組改回只回傳驗證結果(例如一個代表「金額超過門檻」的狀態),通知邏輯留在呼叫驗證模組的上層去處理。程式碼量差不多,差別只在「誰負責決定要不要通知」這件事被放回了正確的層次。
回想你手上系統裡有沒有類似的「順手」修法——為了讓某個功能動起來,讓一個本該獨立的底層模組多知道了一件不該知道的事?那次的 code review,有沒有查證過依賴方向,還是只看了邏輯對不對?
明天要討論一個容易混淆的分界:技術債跟刻意的架構妥協,看起來都是「不完美的設計」,AI 該怎麼分辨這兩者,才不會把刻意的妥協當成技術債清掉,或把技術債當成設計決策放著不管。